import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Monopoly implements Runnable {
	static class Point implements Comparable<Point> {
		long x;
		long y;

		public int compareTo(Point o) {
			long z = x * o.y - y * o.x;
			if (z > 0)
				return -2;
			else if (z < 0)
				return 2;
			else {
				z = Math.abs(x) + Math.abs(y) - Math.abs(o.x) - Math.abs(o.y);
				if (z < 0)
					return -1;
				else if (z > 0)
					return 1;
				else
					return 0;
			}
		}
	}

    private void solve() {
		int n = nextInt();
		Point[] p = new Point[n];
		long mx = Integer.MAX_VALUE;
		long my = Integer.MAX_VALUE;
		int mi = -1;
		for (int i = 0; i < n; ++i) {
			p[i] = new Point();
			p[i].x = nextInt();
			p[i].y = nextInt();
			if (p[i].x < mx || (p[i].x == mx && p[i].y < my)) {
				mx = p[i].x;
				my = p[i].y;
				mi = i;
			}
		}
		for (int i = 0; i < n; ++i) {
			p[i].x -= mx;
			p[i].y -= my;
		}
		int mii = (mi + 1) % n;
		boolean allSame = true;
		for (int i = 0; i < n; ++i) {
			if (p[i].x * p[mii].y != p[i].y * p[mii].x) {
				allSame = false;
				break;
			}
		}
		if (allSame) {
			writer.println(solve1D(n, p, mi));
		} else {
			writer.println(solve2D(n, p, mi));
		}
    }

	private long solve2D(int n, Point[] p, int mi) {
		Arrays.sort(p);
		for (int last = n - 2; last >= 0; --last) {
			if (Math.abs(p[last].compareTo(p[n - 1])) == 2) {
				reverseSubArray(p, last + 1, n - 1);
				break;
			}
		}
		Point[] hull = new Point[n + 1];
		int nHull = 0;
		for (int i = 0; i <= n; ++i) {
			Point cur = i < n ? p[i] : p[0];
			hull[nHull++] = cur;
			while (nHull >= 3) {
				Point a = hull[nHull - 3];
				Point b = hull[nHull - 2];
				Point c = hull[nHull - 1];
				if ((b.x - a.x) * (c.y - b.y) - (b.y - a.y) * (c.x - b.x) >= 0) break;
				hull[nHull - 2] = c;
				--nHull;
			}
		}
		int at = 0;
		long res = 0;
		while (at + 1 < nHull) {
			long dx = hull[at + 1].x - hull[at].x;
			long dy = hull[at + 1].y - hull[at].y;
			int last = at + 1;
			for (int i = at + 2; i < nHull; ++i) {
				long cdx = hull[i].x - hull[at].x;
				long cdy = hull[i].y - hull[at].y;
				if (dx * cdy != dy * cdx)
					break;
				last = i;
			}
			int am = last - at + 1;
			res += (long) am * (am - 1) / 2;
			at = last;
		}
		--nHull;
		res += (long) (n - nHull) * (nHull) + (long) (n - nHull) * (n - nHull - 1) / 2;
		return res;
	}

	private void reverseSubArray(Point[] p, int left, int right) {
		while (left < right) {
			Point tmp = p[left];
			p[left] = p[right];
			p[right] = tmp;
			++left;
			--right;
		}
	}

	private long solve1D(int n, Point[] p, int mi) {
		if (n == 1)
			return 0;
		else
			return 2 * n - 3;
	}

	public static void main(String[] args) {
        new Monopoly().run();
    }

    BufferedReader reader;
    StringTokenizer tokenizer;
    PrintWriter writer;

    static final String TASK_ID = "monopolies";

    public void run() {
        try {
            reader = new BufferedReader(new FileReader(TASK_ID + ".in"));
            tokenizer = null;
            writer = new PrintWriter(TASK_ID + ".out");
            solve();
            reader.close();
            writer.close();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    int nextInt() {
        return Integer.parseInt(nextToken());
    }

    long nextLong() {
        return Long.parseLong(nextToken());
    }

    double nextDouble() {
        return Double.parseDouble(nextToken());
    }

    String nextToken() {
        while (tokenizer == null || !tokenizer.hasMoreTokens()) {
            try {
                tokenizer = new StringTokenizer(reader.readLine());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return tokenizer.nextToken();
    }
}
